"""这段代码展示了如何使用 LangChain 处理大模型（LLM）的输出解析问题，特别是当模型返回不符合预期格式时的修复和重试机制。以下是详细的逻辑解析：
  核心问题：确保大模型的输出符合程序预期的结构化格式（如 JSON 或 Pydantic 模型）。
  挑战：模型可能返回不完整或格式错误的响应（如缺少字段、格式错误）。
  解决方案：
    定义结构化输出格式：用 Pydantic 模型明确描述期望的输出字段。
    自动修复错误响应：通过 OutputFixingParser 直接修复格式错误的输出。
    重试机制：通过 RetryWithErrorOutputParser 将错误反馈给模型，请求重新生成正确输出。
"""
# 定义一个模板字符串，这个模板将用于生成提问
template = """Based on the user question, provide an Action and Action Input for what step should be taken.
{format_instructions}
Question: {query}
Response:"""

# 定义一个Pydantic数据格式，这个格式描述了一个"行动"类及其属性
from pydantic import BaseModel, Field
"""Pydantic模型定义：规定输出的结构化格式，包含两个字段（action与action_input）
Pydantic 的优势：自动验证字段类型和必填性"""
class Action(BaseModel):
    action: str = Field(description="action to take")
    action_input: str = Field(description="input to the action")

# 使用Pydantic格式Action来初始化一个输出解析器
from langchain.output_parsers import PydanticOutputParser
# 输出解析器初始化: 将 Pydantic 模型转换为 LangChain 的解析器，用于校验和解析模型输出
parser = PydanticOutputParser(pydantic_object=Action)

# 定义一个提示模板，它将用于向模型提问
from langchain.prompts import PromptTemplate
"""提示模板构建：生成最终的提示词，合并用户问题和格式要求"""
prompt = PromptTemplate(
    template="Answer the user query.\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)
prompt_value = prompt.format_prompt(query="What are the colors of Orchid?")

# 定义一个错误格式的字符串（故意给出缺字段 JSON）
bad_response = '{"action": "search"}'
# parser.parse(bad_response) # 如果直接解析，它会引发一个错误

# 设置OpenAI API密钥
import os
from dotenv import load_dotenv  # 用于加载环境变量
load_dotenv()  # 加载 .env 文件中的环境变量

# 尝试用OutputFixingParser来解决这个问题
from langchain.output_parsers import OutputFixingParser
from langchain_community.chat_models import ChatZhipuAI
"""错误修复解析器 (OutputFixingParser)：
  逻辑：
    检测到 bad_response 不符合 Action 格式。
    自动调用大模型（ChatZhipuAI）修复缺失字段，生成完整响应（如补全 action_input）。
  适用场景：快速修复轻微格式错误，无需重新调用主流程。
"""
fix_parser = OutputFixingParser.from_llm(parser=parser, llm=ChatZhipuAI(api_key=os.getenv("ZHIPUAI_API_KEY")))
parse_result = fix_parser.parse(bad_response)
print('OutputFixingParser的parse结果:',parse_result)

"""重试解析器 (RetryWithErrorOutputParser)，初始化RetryWithErrorOutputParser，它会尝试再次提问来得到一个正确的输出
  逻辑：
    将原始错误响应和原始提示（prompt_value）一起发送给大模型。
    要求模型根据错误信息重新生成符合格式的响应。
    适用场景：错误较复杂时，通过反馈机制让模型自我修正。
"""
from langchain.output_parsers import RetryWithErrorOutputParser
retry_parser = RetryWithErrorOutputParser.from_llm(
    parser=parser, llm=ChatZhipuAI(api_key=os.getenv("ZHIPUAI_API_KEY"), temperature=0)
)
parse_result = retry_parser.parse_with_prompt(bad_response, prompt_value)
print('RetryWithErrorOutputParser的parse结果:',parse_result)
"""这段代码演示了“如何让大模型始终吐出结构化 JSON”，并在出错时自动修复或重试，确保下游逻辑零崩溃
  可以把这段思路套用到任何 需要机器返回固定格式 的场景，比如：
    自动化工作流（Action=“send_email”, ActionInput=“收件人+内容”）
    函数调用（Action=“query_db”, ActionInput=“SQL 语句”）
    多轮 Agent 决策（Action=“search”, ActionInput=“关键词”）
关键技术与设计模式:
  结构化输出控制：
    使用 Pydantic 强制定义输出格式，避免非结构化文本的解析困难。
  错误处理分层：
    修复：轻微错误时自动补全字段。
    重试：严重错误时重新生成响应。
  提示工程：
    通过 format_instructions 动态嵌入格式要求，指导模型输出。
"""